package com.tools.web.interceptor.auth;

import com.tools.common.exception.ParseRuntimeException;
import com.tools.common.object.Note;
import com.tools.common.object.string.StrKit;
import com.tools.common.object.string.VerifyKit;
import com.tools.common.security.secret.Cypher;
import com.tools.common.thread.Timeunit;

import java.util.Objects;

/**
 * AuthToken 的规范
 * */
@Note("AuthToken 的规范")
public final class AuthToken {

    @Note("UUID 标识前缀")
    private String uuid;

    @Note("用户/账户 ID")
    private String userId;

    @Note("客户端设备类型 ID")
    private String deviceTypeId;

    @Note("用户类型标识。（比如平台用户、小程序用户等等）")
    private String userType;

    @Note("用户/账户的登录时间戳")
    private long loginTime;

    @Note("token 已经续期的次数")
    private int extendedCount;

    @Note("token 具体过期的时间临界点")
    private long expiration;

    @Note("以下各部分之间的分隔符")
    private String separator = ":";

    @Note("token 用于加密解密的工具实例")
    private final Cypher cypher;

    /* **************************************************************************************
     *
     *          构造器
     *
     * **************************************************************************************
     * */

    public AuthToken(Cypher cypher) {
        if(cypher == null) throw new NullPointerException("【token 用于加密解密的工具实例】 不能为 null");
        this.cypher = cypher;
        this.userType = "";
    }

    public AuthToken(Cypher cypher, String separator) {
        this(cypher);
        this.setSeparator(separator);
    }

    public AuthToken(String encryptedToken, Cypher cypher) {
        this(cypher);
        this.decrypt(encryptedToken);
    }

    public AuthToken(String encryptedToken, Cypher cypher, String separator) {
        this(cypher, separator);
        this.decrypt(encryptedToken);
    }

    /* **************************************************************************************
     *
     *          Getter / Setter
     *
     * **************************************************************************************
     * */

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        if(uuid == null || uuid.isEmpty()) throw new NullPointerException("不能设置空的 UUID");
        this.uuid = uuid;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        if(userId == null || userId.isEmpty()) throw new NullPointerException("不能设置空的用户 ID");
        this.userId = userId;
    }

    public void setUserId(long userId) {
        this.userId = Long.toString(userId);
    }

    public String getDeviceTypeId() {
        return deviceTypeId;
    }

    public void setDeviceTypeId(String deviceTypeId) {
        if(deviceTypeId == null || deviceTypeId.isEmpty()) throw new NullPointerException("不能设置空的客户端设备 ID");
        this.deviceTypeId = LoginDeviceTypeEnum.getEnum(deviceTypeId).getId();
    }

    public void setDeviceTypeId(LoginDeviceTypeEnum deviceType) {
        if(deviceType == null) throw new NullPointerException();
        this.deviceTypeId = deviceType.getId();
    }

    public String getUserType() {
        return userType;
    }

    public String userTypeAsPrefix() {
        return userType.isEmpty() ? "" : separator + userType;
    }

    public void setUserType(String userType) {
        this.userType = (userType == null) ? "" : userType;
    }

    public long getLoginTime() {
        return loginTime;
    }

    public void setLoginTime(long loginTime) {
        this.loginTime = loginTime;
    }

    public int getExtendedCount() {
        return extendedCount;
    }

    public void setExtendedCount(int extendedCount) {
        this.extendedCount = extendedCount;
    }

    public long getExpiration() {
        return expiration;
    }

    public void setExpiration(long expiration) {
        this.expiration = expiration;
    }

    public String getSeparator() {
        return separator;
    }

    public void setSeparator(String separator) {
        this.separator = (separator == null || separator.isEmpty())
                ? this.separator
                : separator;
    }

    public Cypher getCypher() {
        return cypher;
    }


    @Override
    public String toString() {
        return uuid + separator + userId + separator +
                deviceTypeId + separator + userType+ separator +
                loginTime + separator + extendedCount + separator + expiration;

    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AuthToken authToken = (AuthToken) o;
        if(!Objects.equals(userId, authToken.userId)) return false;
        if(!Objects.equals(deviceTypeId, authToken.deviceTypeId)) return false;
        if(!Objects.equals(userType, authToken.userType)) return false;
        return Objects.equals(uuid, authToken.uuid);
    }

    @Override
    public int hashCode() {
        return Objects.hash(uuid, userId, deviceTypeId, userType);
    }

    /* **************************************************************************************
     *
     *          功能方法
     *
     * **************************************************************************************
     * */

    @Note("解密 token，将解析后得到的各个部分赋值到属性中")
    public void decrypt(String encryptedToken) {
        String s = this.cypher.decrypt(encryptedToken);
        String[] a = StrKit.split(s, this.separator);
        if(a.length < 6) {
            throw new ParseRuntimeException("token 解析失败，不符合 AuthToken 对象的规范");
        }
        this.uuid = a[0];
        this.deviceTypeId = LoginDeviceTypeEnum.getEnum(a[1]).getId();
        this.userId = a[2];
        this.userType = (a[3] == null || "null".equals(a[3])) ? "" : a[3];
        if(!VerifyKit.isAllInteger(a[4], a[5], a[6])) {
            throw new ParseRuntimeException("token 解析失败，不符合 AuthToken 对象的规范");
        }
        this.loginTime = Long.parseLong(a[4]);
        this.extendedCount = Integer.parseInt(a[5]);
        this.expiration = Long.parseLong(a[6]);
    }

    @Note("加密 token")
    public String encrypt() {
        this.private_checkPropertyIsNotEmpty();
        String separator = this.separator;
        String s = this.uuid + separator + this.deviceTypeId + separator +
                this.userId + separator + this.userType + separator +
                this.loginTime + separator + this.extendedCount + separator + this.expiration;
        return this.cypher.encrypt(s);
    }


    @Note("判断 token 是否已过期")
    public boolean ifExpired(Timeunit expirationTimeunit) {
        return expirationTimeunit.cast(System.currentTimeMillis(), Timeunit.MILL) >= this.expiration;
    }

    /* **************************************************************************************
     *
     *          私有逻辑
     *
     * **************************************************************************************
     * */

    @Note("校验部分重要的属性值")
    private void private_checkPropertyIsNotEmpty() {
        if(uuid == null || uuid.isEmpty()) throw new NullPointerException("UUID 部分为空");
        if(userId == null || userId.isEmpty()) throw new NullPointerException("用户 ID 部分为空");
        if(deviceTypeId == null || deviceTypeId.isEmpty()) throw new NullPointerException("设备类型标识为空");
    }
}
